#pragma once

#include <memory>
#include <string>
#include <vector>
#include <optional>

#include "backend/kernel_interface/kernel_scheduler.h"
#include "backend/device/txda_device.h"
#include "backend/target_properties.h"
// #include "backend/program/program_mainc_group.h"

// #include <TxDA/txdac.h>
// typedef void* txdaModule_t;

template <class F, F f>
struct manage_deleter {
  template <class T>
  void operator()(T* x) const {
    if (x != nullptr) {
      f(x);
    }
  }
};

template <class T, class F, F f>
using manage_ptr = std::unique_ptr<T, manage_deleter<F, f>>;

#define TXDNN_MANAGE_PTR(T, F) \
  manage_ptr<typename std::remove_pointer<T>::type, decltype(&F), &F>

// using TxdaModulePtr = TXDNN_MANAGE_PTR(txdaModule_t, txdaModuleUnload);


namespace txdnn {
namespace backend {

struct TmpDir {
  explicit TmpDir(TXDNN_STR_VIEW prefix = "");
  TmpDir(TmpDir&&) = default;
  TmpDir& operator=(TmpDir&&) = default;
  ~TmpDir();

  fs::path operator/(TXDNN_STR_VIEW other) const { return path_ / other; }

  operator const fs::path&() const { return path_; }
  operator std::string() const { return path_.string(); }

  int Execute(TXDNN_STR_VIEW cmd, TXDNN_STR_VIEW args) const;

  fs::path path_;
};



/**
 * @brief struct ProgramImplMainCGroup
 *        call txdaModule_t to build code object
 */
struct ProgramImplMainCGroup {
  ProgramImplMainCGroup() {};
  ProgramImplMainCGroup(const fs::path& program_path, const fs::path& file_bin);
  ProgramImplMainCGroup(const fs::path& program_path, const std::vector<char>& blob);
  ProgramImplMainCGroup(const fs::path& program_path, const std::vector<uint8_t>& blob);
  ProgramImplMainCGroup(const fs::path& program_path, std::string params, const TargetProperties& target,
                        const std::string& kernel_src);

  fs::path program_path_;
  TargetProperties target_;
  fs::path bin_file_;
  // TxdaModulePtr module_;
  std::vector<char> binary_;
  std::optional<TmpDir> dir_;

public:
  void BuildCodeObjectFromFile(std::string& params, TXDNN_STR_VIEW src, const fs::path& p);
  void BuildCodeObjectFromMemory(const std::string& params, TXDNN_STR_VIEW src, const fs::path& p);
  void BuildCodeObject(std::string params, const std::string& kernel_src);
};

} // namespace backend
} // namespace txdnn